{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "[source](../../api/alibi_detect.od.sr.rst)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Spectral Residual"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Overview\n",
    "\n",
    "The Spectral Residual outlier detector is based on the paper [Time-Series Anomaly Detection Service at Microsoft](https://arxiv.org/abs/1906.03821) and is suitable for **unsupervised online anomaly detection in univariate time series** data. The algorithm first computes the [Fourier Transform](https://en.wikipedia.org/wiki/Fourier_transform) of the original data. Then it computes the *spectral residual* of the log amplitude of the transformed signal before applying the Inverse Fourier Transform to map the sequence back from the frequency to the time domain. This sequence is called the *saliency map*. The anomaly score is then computed as the relative difference between the saliency map values and their moving averages. If the score is above a threshold, the value at a specific timestep is flagged as an outlier. For more details, please check out the [paper](https://arxiv.org/abs/1906.03821)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Usage\n",
    "\n",
    "### Initialize\n",
    "\n",
    "Parameters:\n",
    "\n",
    "* `threshold`: Threshold used to classify outliers. Relative saliency map distance from the moving average.\n",
    "\n",
    "* `window_amp`: Window used for the moving average in the *spectral residual* computation. The spectral residual is the difference between the log amplitude of the Fourier Transform and a convolution of the log amplitude over `window_amp`.\n",
    "\n",
    "* `window_local`: Window used for the moving average in the outlier score computation. The outlier score computes the relative difference between the saliency map and a moving average of the saliency map over `window_local` timesteps.\n",
    "\n",
    "* `padding_amp_method`:\n",
    "    Padding method to be used prior to each convolution over log amplitude.\n",
    "    Possible values: `constant` | `replicate` | `reflect`. Default value: `replicate`.\n",
    "\n",
    "     - `constant` - padding with constant 0.\n",
    "\n",
    "     - `replicate` - repeats the last/extreme value.\n",
    "\n",
    "     - `reflect` - reflects the time series.\n",
    "\n",
    "* `padding_local_method`:\n",
    "    Padding method to be used prior to each convolution over saliency map.\n",
    "    Possible values: `constant` | `replicate` | `reflect`. Default value: `replicate`.\n",
    "\n",
    "     - `constant` - padding with constant 0.\n",
    "\n",
    "     - `replicate` - repeats the last/extreme value.\n",
    "\n",
    "     - `reflect` - reflects the time series.\n",
    "\n",
    "* `padding_amp_side`:\n",
    "    Whether to pad the amplitudes on both sides or only on one side.\n",
    "    Possible values: `bilateral` | `left` | `right`.\n",
    "\n",
    "* `n_est_points`: Number of estimated points padded to the end of the sequence.\n",
    "\n",
    "* `n_grad_points`: Number of points used for the gradient estimation of the additional points padded to the end of the sequence. The paper sets this value to 5.\n",
    "\n",
    "Initialized outlier detector example:\n",
    "\n",
    "```python\n",
    "from alibi_detect.od import SpectralResidual\n",
    "\n",
    "od = SpectralResidual(\n",
    "    threshold=1.,\n",
    "    window_amp=20,\n",
    "    window_local=20,\n",
    "    padding_amp_method='reflect',\n",
    "    padding_local_method='reflect',\n",
    "    padding_amp_side='bilateral',\n",
    "    n_est_points=10,\n",
    "    n_grad_points=5\n",
    ")\n",
    "```\n",
    "\n",
    "It is often hard to find a good threshold value. If we have a time series containing both normal and outlier data and we know approximately the percentage of normal data in the time series, we can infer a suitable threshold:\n",
    "\n",
    "```python\n",
    "od.infer_threshold(\n",
    "    X,\n",
    "    t=t,  # array with timesteps, assumes dt=1 between observations if omitted\n",
    "    threshold_perc=95\n",
    ")\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Detect\n",
    "\n",
    "We detect outliers by simply calling `predict` on a time series `X` to compute the outlier scores and flag the anomalies. We can also return the instance (timestep) level outlier score by setting `return_instance_score` to True.\n",
    "\n",
    "The prediction takes the form of a dictionary with `meta` and `data` keys. `meta` contains the detector's metadata while `data` is also a dictionary which contains the actual predictions stored in the following keys:\n",
    "\n",
    "* `is_outlier`: boolean whether instances are above the threshold and therefore outlier instances. The array is of shape *(timesteps,)*.\n",
    "\n",
    "* `instance_score`: contains instance level scores if `return_instance_score` equals True.\n",
    "\n",
    "\n",
    "```python\n",
    "preds = od.predict(\n",
    "    X,\n",
    "    t=t,  # array with timesteps, assumes dt=1 between observations if omitted\n",
    "    return_instance_score=True\n",
    ")\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Examples\n",
    "\n",
    "[Time series outlier detection with Spectral Residuals on synthetic data](../../examples/od_sr_synth.ipynb)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.14"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
